local f = CreateFrame("Button", "JerryCrushFrame", UIParent)

f:SetSize(128, 32)
f:SetToplevel(true)

local function CreateTexture(f, name, _, r, g, b)
	local t = f:CreateTexture(nil, "BACKGROUND")
	f[name] = t
	t:SetTexture(r, g, b, .5)
	t:SetHeight(f:GetHeight())
end

local types = {
	{ "parry", nil, 1.00, 0.45, 0.90 }, -- ffe674
	{ "dodge", nil, 0.45, 0.78, 1.00 }, -- 74c6ff
	{ "miss",  nil, 0.91, 1.00, 0.60 }, -- e8ff98
	{ "block", nil, 0.18, 0.63, 1.00 }, -- 2fa0ff
	{ "crit",  nil, 1.00, 0.18, 0.18 }, -- ff2f2f
	{ "crush", nil, 0.76, 0.24, 1.00 }, -- c23eff
	{ "hit",   nil, 0.59, 0.00, 0.00 }, -- 960000
}

for _, t in pairs(types) do
	CreateTexture(f, unpack(t))
end

local text = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
text:SetAllPoints(f)
text:SetJustifyH("CENTER")
text:SetJustifyV("MIDDLE")

function f:ToggleLocked()
	if self:IsMouseEnabled() then
		self:SetBackdrop(nil)
		self:EnableMouse(false)
		self:SetMovable(false)
		self:RegisterForClicks()
		self:RegisterForDrag()
	else
		self:SetBackdrop({
				bgFile = "Interface\\TutorialFrame\\TutorialFrameBackground",
				edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
				tile = true,
				edgeSize = 16,
				tileSize = 32,
				insets = {
						left = 5,
						right = 5,
						top = 5,
						bottom = 5
				}
		})
		self:EnableMouse(true)
		self:SetMovable(true)
		self:RegisterForClicks"LeftButtonUp"
		self:RegisterForDrag"LeftButton"
	end
end

local scripts = {}

function scripts:OnClick(button)
	if button == "LeftButton" then
		self:ToggleLocked()
	end
end

function scripts:OnDragStart()
	self:StartMoving()
end

function scripts:OnDragStop()
	self:StopMovingOrSizing()
	local x, y = self:GetCenter()
	local dx, dy = UIParent:GetCenter()
	JerryDB.crushPos.offsetx, JerryDB.crushPos.offsety = x - dx, y - dy
end

function scripts:OnShow()
	self:GetScript("OnEvent")(self)
end

function scripts:OnEvent(_, name)
	if name:lower() == "jerry" then
		if JerryDB.crushPos then
			f:SetPoint("CENTER", JerryDB.crushPos.offsetx, JerryDB.crushPos.offsety)
		else
			f:SetPoint("CENTER")
			local x, y = self:GetCenter()
			local dx, dy = UIParent:GetCenter()
			JerryDB.crushPos = {
				offsetx = x - dx,
				offsety = y - dy,
			}
			f:ToggleLocked()
		end
		self:SetScript("OnEvent", function (self, event, unit)
			if event == "SPELLS_CHANGED" then return self:SPELLS_CHANGED() end
			if event ~= "UNIT_AURA" or unit == "player" then
				if UnitExists("target") then
					self:Show()
					self:Update()
				else
					self:Hide()
				end
			end
		end)
		self:UnregisterEvent("ADDON_LOADED")
		self:RegisterEvent("PLAYER_TARGET_CHANGED")
		self:RegisterEvent("UNIT_AURA")
		self:RegisterEvent("SPELLS_CHANGED")
		self:SPELLS_CHANGED()
	end
end

do
	local UnitExists = UnitExists
	local UnitLevel = UnitLevel
	local UnitDefense = UnitDefense
	local UnitIsPlayer = UnitIsPlayer
	local GetDodgeChance = GetDodgeChance
	local GetBlockChance = GetBlockChance
	local GetParryChance = GetParryChance
	local GetSpellName = GetSpellName
	local BOOKTYPE_SPELL = BOOKTYPE_SPELL
	local format = string.format
	local pairs = pairs

	local get_percent = function (v, sum)
		if v < 0 then
			return 0, sum
		elseif v > sum then
			return sum, 0
		else
			return v, sum - v
		end
	end

	local can_block, can_parry
	local block_spell, parry_spell = GetSpellInfo(107), GetSpellInfo(3127)
	function f:SPELLS_CHANGED()
		can_block, can_parry = false, false
		local i = 1
		while true do
			local name = GetSpellName(i, BOOKTYPE_SPELL)
			if not name then break end
			if name == block_spell then can_block = true end
			if name == parry_spell then can_parry = true end
			i = i + 1
		end
	end

	local function get_diminishing_returns(v, r)
		return 1 / (1 / r + 0.9560 / v)
	end

	local function update_combat_table()
		local playerLevel, targetLevel = UnitLevel"player", UnitLevel"target"
		if targetLevel == -1 then targetLevel = playerLevel + 3 end
--[[
		local baseDefense, bonusDefense = UnitDefense"player"
		local playerDefense = baseDefense + bonusDefense
		local targetWeaponSkill = targetLevel * 5
		local targetLevelBonus = (targetWeaponSkill - baseDefense) * .04
		local playerDefBonus = (playerDefense - targetWeaponSkill) * .04
		local defenseBonus = playerDefBonus - targetLevelBonus
]]
		local defenseBonus = GetCombatRating(CR_DEFENSE_SKILL) / 122.962

		local parryChance, dodgeChance, missChance, blockChance,
				critChance, crushChance, hitChance

		local total = 100 + (targetLevel - playerLevel) * .8 -- == .04 * 4 * 5
		parryChance, total = get_percent(can_parry and GetParryChance()  or 0, total) -- parry
		dodgeChance, total = get_percent(GetDodgeChance(), total)-- dodge
		missChance, total = get_percent(5 + get_diminishing_returns(defenseBonus, 16), total) -- miss
		blockChance, total = get_percent(can_block and GetBlockChance() or 0, total) -- block
		critChance, total = get_percent(5 - defenseBonus, total) -- crit
		crushChance, total = get_percent((targetLevel > playerLevel + 3 and not UnitIsPlayer"target") and 15 or 0, total) -- crush
		hitChance = total

		types[1][2] = parryChance -- parry
		types[2][2] = dodgeChance-- dodge
		types[3][2] = missChance -- miss
		types[4][2] = blockChance -- block
		types[5][2] = critChance -- crit
		types[6][2] = crushChance -- crush
		types[7][2] = hitChance -- hit

		return parryChance + dodgeChance + missChance + blockChance,
				critChance, crushChance
	end

	function f:Update()
		local start = 0
		local width = self:GetWidth()
		local avoidance, critChance, crushChance = update_combat_table()

		for _, data in pairs(types) do
			local type, value = unpack(data)
			local texture = self[type]
			if value == 0 or start >= width then
				texture:Hide()
			else
				value = value * width / 100
				local stop = start + value
				if stop > width then
					value = width - start
				end
				texture:Show()
				texture:SetPoint("LEFT", start, 0)
				texture:SetWidth(value)
				start = start + value
			end
		end

		if critChance > 0 then
			if crushChance > 0 then
				text:SetFormattedText("|cffff0000%.1f%%|r / |cffff8000%.1f%%|r", critChance, crushChance)
			else
				text:SetFormattedText("|cffff0000%.1f%%|r", critChance)
			end
		elseif crushChance > 0 then
			text:SetFormattedText("|cffff8000%.1f%%|r", crushChance)
		elseif avoidance < 100 then
			text:SetFormattedText("|cff00ff00%.1f%%|r", avoidance)
		else
			text:SetFormattedText("|cffffffff%.1f%%|r", avoidance)
		end
	end
end

for name, handler in pairs(scripts) do
	f:SetScript(name, handler)
end

f:RegisterEvent("ADDON_LOADED")

SlashCmdList.JERRY_CRUSH = function (input)
	JerryCrushFrame:ToggleLocked()
end
SLASH_JERRY_CRUSH1="/crush"
